package com.njcb.ams.scheduler.bean;

import com.njcb.ams.pojo.enumvalue.JobResult;
import com.njcb.ams.portal.SysBaseDefine;
import com.njcb.ams.repository.dao.SysTaskJobDAO;
import com.njcb.ams.repository.dao.SysTaskJobLogDAO;
import com.njcb.ams.repository.entity.SysTaskJob;
import com.njcb.ams.repository.entity.SysTaskJobLog;
import com.njcb.ams.scheduler.utils.JobConstant;
import com.njcb.ams.util.AmsDateUtils;
import com.njcb.ams.util.AmsJsonUtils;
import com.njcb.ams.util.AmsUtils;
import org.apache.commons.lang.StringUtils;
import org.quartz.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.MutablePropertyValues;
import org.springframework.beans.PropertyAccessorFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

/**
 * 
 * @author 刘彦龙
 *
 */
@PersistJobDataAfterExecution //执行后刷新 jobmap
@DisallowConcurrentExecution  //不允许并发执行
public abstract class BaseJobDetail implements Job {
	private static final Logger logger = LoggerFactory.getLogger(BaseJobDetail.class);
	@Autowired
	private PlatformTransactionManager transactionManager;
	@Autowired
	private SysTaskJobDAO sysTaskJobDAO;
	@Autowired
	private SysTaskJobLogDAO sysTaskJobLogDAO;

	@Override
	public final void execute(JobExecutionContext context) throws JobExecutionException {
		try {
			BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
			MutablePropertyValues pvs = new MutablePropertyValues();
			pvs.addPropertyValues(context.getScheduler().getContext());
			pvs.addPropertyValues(context.getMergedJobDataMap());
			bw.setPropertyValues(pvs, true);
		} catch (SchedulerException ex) {
			throw new JobExecutionException(ex);
		}
		jobFilter(context);
		executeInternal(context);
	}
	
	/**
	 * 实现定制化的任务调度
	 * @param context
	 */
	private void jobFilter(JobExecutionContext context) {
//		JobDataMap jobContext = context.getJobDetail().getJobDataMap();
//		ScheduleJob scheduleJob = (ScheduleJob)jobContext.get(JobConstant.JOBBEAN_KEY);
	}


	public abstract JobResult executeJob(JobExecutionContext context) throws Exception;

	public void afterReturning(JobExecutionContext context, JobResult result) throws Exception{
		
	};
	
	public void afterThrowable(JobExecutionContext context, JobResult result){
		
	};

	private void executeInternal(JobExecutionContext context) throws JobExecutionException {
		JobDataMap jobContext = context.getJobDetail().getJobDataMap();
		Object taskBean = jobContext.get(JobConstant.JOBBEAN_KEY);
		//通过json序列号拿到ScheduleJob，避免不同类加载器导致的ClassCastException
		ScheduleJob scheduleJob = AmsJsonUtils.jsonToObject(AmsJsonUtils.objectToJson(taskBean),ScheduleJob.class);
		if (null == scheduleJob) {
			logger.error("#######任务任务不存在");
			return;
		}
		SysTaskJob jobBean = sysTaskJobDAO.selectByPrimaryKey(scheduleJob.getJobId());
		if (null == jobBean) {
			logger.error("#######任务[{}] 任务不存在", scheduleJob.getJobId());
			return;
		}
		JobResult result = JobResult.RESULT_IGNORE;
		boolean hasJobFinished = false;// 任务是否执行结束
		String message = "";
		logger.info("#######任务[{}] 此次轮询开始  {}#######", jobBean.getRemark(), LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss")));
		TransactionStatus status = null;
		try {
			
			DefaultTransactionDefinition def = new DefaultTransactionDefinition();
			def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
			status = transactionManager.getTransaction(def);
			
			result = executeJob(context);
			if (null == result) {
				result = JobResult.RESULT_SUCCESS;
			}
			transactionManager.commit(status);
		} catch (Throwable e) {
			transactionManager.rollback(status);
			if(null != e.getCause()){
				e = e.getCause();
			}
			logger.error(e.getMessage(),e);
			message = e.getMessage();
			result = JobResult.RESULT_FAIL;
		}

		String dateTime = AmsDateUtils.getCurrentTime14();
		try {
			//成功_轮询未结束则继续轮询
			if (result.equals(JobResult.RESULT_SUCCESS)) {
				logger.info("#########任务[{}] 执行成功  {}#########", jobBean.getJobName(), dateTime);
			}
			// 成功并提前结束 不再轮询
			if (result.equals(JobResult.RESULT_SUCCESS_TO_END)) {
				logger.info("#######任务[{}] 成功并提前结束此次轮询  {}#######", jobBean.getJobName(), dateTime);
			}
			//失败_轮询未结束则继续轮询
			if (result.equals(JobResult.RESULT_FAIL)) {
				logger.info("#########任务[{}] 执行失败  {}#########", jobBean.getJobName(), dateTime);
			}
			//失败并提前结束 不再轮询
			if (result.equals(JobResult.RESULT_FAIL_TO_END)) {
				logger.info("#########任务[{}] 执行失败  {}#########", jobBean.getJobName(), dateTime);
			}
			// 忽略的返回 忽略不计入成功次数
			if (result.equals(JobResult.RESULT_IGNORE)) {
				logger.info("#######任务[{}] 忽略此次轮询  {}#######", jobBean.getJobName(), dateTime);
			}

			// 执行后处理
			afterReturning(context, result);
			logger.info("#########job[{}] 轮询执行第{}次结束  {}#########", jobBean.getJobName(), jobBean.getRepeatCount(), dateTime);
		} catch (Throwable e) {
			afterThrowable(context, result);
			message = e.getMessage();
			logger.error("#########job[{}] 轮询出错  {}#########", jobBean.getJobName(), dateTime);
			logger.error(e.getMessage(), e);
//			throw new JobExecutionException(e);
		} finally {
			afterExeInternal(jobBean, result, message, hasJobFinished);
		}
	}

	
	/** 执行结束后的操作 */
	private void afterExeInternal(SysTaskJob jobBean, JobResult result, String message, boolean isFinished) {
		SysTaskJobLog log = generateLog(jobBean, result, message, isFinished);
		sysTaskJobLogDAO.insert(log);
		jobBean.setLastRunTime(AmsDateUtils.getCurrentTime14());
		sysTaskJobDAO.updateLastRunTime(jobBean.getId(),jobBean.getLastRunTime());
	}

	/** 记入日志表 **/
	private SysTaskJobLog generateLog(SysTaskJob jobBean, JobResult result, String message, boolean isFinished) {
		SysTaskJobLog log = new SysTaskJobLog();
		message = StringUtils.isEmpty(message)?"":message;
		log.setExceptionMsg(message.length() >= 512 ? message.substring(512) : message);
		log.setExcuteResult(AmsUtils.isNull(result)?"":result.getCode());
		log.setExcuteNode(SysBaseDefine.TRADE_NODE);
		log.setExcuteTime(AmsDateUtils.getCurrentTime14());
		log.setJobName(jobBean.getJobName());
		log.setProcessFunction(jobBean.getProcessFunction());
		return log;
	}
}
